﻿using System;
using System.Collections.Generic;
using System.Text;

public class BoardService
{
    private const int k_FirstRow = 0;
    private const int k_ColumnIsFull = 2;
    private const int k_Continue = 4;
    private const int k_Win = 0;
    private const int k_ConnectedFour = 4;
    private readonly Cell[,] r_BoardMatrix = null;
    private int m_BoardMatrixRowSize = 0;
    private int m_BoardMatrixColSize = 0;
    private int m_DiscsCounter = 0;

    internal int DiscsCounter
    {
        get { return m_DiscsCounter; }
        set { m_DiscsCounter = value; }
    }

    private int m_MaxDiscsInBoard = 0;

    internal Cell[,] BoardMatrix
    {
        get { return r_BoardMatrix; }
    }

    public int BoardMatrixRowSize
    {
        get { return m_BoardMatrixRowSize; }
    }

    public int BoardMatrixColSize
    {
        get { return m_BoardMatrixColSize; }
    }

    public BoardService(int i_BoardRowSize, int i_BoardColSize)
    {
        m_BoardMatrixRowSize = i_BoardRowSize;
        m_BoardMatrixColSize = i_BoardColSize;
        m_MaxDiscsInBoard = i_BoardRowSize * i_BoardColSize;
        r_BoardMatrix = new Cell[m_BoardMatrixRowSize, m_BoardMatrixColSize];
        initializeEmptyBoard();
    }

    internal void MakeEmptyBoard()
    {
        foreach (Cell CurrentCell in r_BoardMatrix)
        {
            CurrentCell.CellSymbol = Cell.eCell.Empty;
        }
    }

    private void initializeEmptyBoard()
    {
        for (int CurrentRowIndex = 0; CurrentRowIndex < m_BoardMatrixRowSize; CurrentRowIndex++)
        {
            for (int CurrentColIndex = 0; CurrentColIndex < m_BoardMatrixColSize; CurrentColIndex++)
            {
                r_BoardMatrix[CurrentRowIndex, CurrentColIndex] = new Cell(CurrentRowIndex, CurrentColIndex);
            }
        }
    }

    public GameEngine.eGameMoveStatus AddDisc(int i_ChosenColumn, int i_CurrentPlayerIndex)
    {
        GameEngine.eGameMoveStatus? AddDiscResult = null;
        i_ChosenColumn--;

        int RowOfLowerEmptyCellInColumn = getLowestEmptyCellInColumn(i_ChosenColumn);

        if (RowOfLowerEmptyCellInColumn < 0)
        {
            AddDiscResult = GameEngine.eGameMoveStatus.ColumnIsFull;
        }
        else
        {
            Cell CellToUpdate = r_BoardMatrix[RowOfLowerEmptyCellInColumn, i_ChosenColumn];
            CellToUpdate.CellSymbol = (Cell.eCell)i_CurrentPlayerIndex;
            m_DiscsCounter++;

            if (CheckIfWinner(RowOfLowerEmptyCellInColumn, i_ChosenColumn))
            {
                AddDiscResult = GameEngine.eGameMoveStatus.PlayerWin;
            }
            else if (m_DiscsCounter.Equals(m_MaxDiscsInBoard))
            {
                AddDiscResult = GameEngine.eGameMoveStatus.BoardIsFull;
            }
            else if (RowOfLowerEmptyCellInColumn == k_FirstRow)
            {
                AddDiscResult = GameEngine.eGameMoveStatus.ColumnIsFull;
            }
            else
            {
                AddDiscResult = GameEngine.eGameMoveStatus.Continue;
            }
        }

        return AddDiscResult.Value;
    }

    private int getLowestEmptyCellInColumn(int i_ChosenColumn)
    {
        int LastEmptyCellInColumn = 0;
        bool FoundLowerEmptyCellInColumn = false;

        while (FoundLowerEmptyCellInColumn == false)
        {
            Cell CurrentCell = r_BoardMatrix[LastEmptyCellInColumn, i_ChosenColumn];

            if (CurrentCell.CellSymbol.Equals(Cell.eCell.Empty) && LastEmptyCellInColumn == m_BoardMatrixRowSize - 1)
            {
                FoundLowerEmptyCellInColumn = true;
            }
            else if (CurrentCell.CellSymbol.Equals(Cell.eCell.Empty))
            {
                LastEmptyCellInColumn++;
            }
            else if (!CurrentCell.CellSymbol.Equals(Cell.eCell.Empty))
            {
                LastEmptyCellInColumn--;
                FoundLowerEmptyCellInColumn = true;
            }
        }

        return LastEmptyCellInColumn;
    }

    public bool CheckIfWinner(int i_LastMoveRow, int i_LastMoveCol)
    {
        bool win = false;
        Cell LastMoveCell = r_BoardMatrix[i_LastMoveRow, i_LastMoveCol];

        if (isConnectFourInRow(LastMoveCell, i_LastMoveRow))
        {
            win = true;
        }
        else if (isConnectFourInColumn(LastMoveCell, i_LastMoveCol))
        {
            win = true;
        }
        else if (isConnectFourInLeftDiagonal(LastMoveCell, i_LastMoveRow, i_LastMoveCol))
        {
            win = true;
        }
        else if (isConnectFourInRightDiagonal(LastMoveCell, i_LastMoveRow, i_LastMoveCol))
        {
            win = true;
        }

        return win;
    }

    private bool isConnectFourInLeftDiagonal(Cell LastMoveCell, int i_LastMoveRow, int i_LastMoveCol)
    {
        bool IsConnectFourInLeftDiagonalResult = false;
        int StartRow = i_LastMoveRow;
        int StartCol = i_LastMoveCol;
        int CurrentCol = 0;
        int SequenceCounter = 0;
        Cell CurrentCellToCheck = null;

        while ((StartRow > 0) && (StartCol > 0))
        {
            StartRow--;
            StartCol--;
        }

        CurrentCol = StartCol;

        for (int CurrentRowIndex = StartRow; (CurrentRowIndex < m_BoardMatrixRowSize) && (CurrentCol < m_BoardMatrixColSize) && (IsConnectFourInLeftDiagonalResult == false); CurrentRowIndex++, CurrentCol++)
        {
            CurrentCellToCheck = r_BoardMatrix[CurrentRowIndex, CurrentCol];
            
            if (isSamePlayerSymbol(LastMoveCell, CurrentCellToCheck))
            {
                SequenceCounter++;
                if (SequenceCounter == k_ConnectedFour)
                {
                    IsConnectFourInLeftDiagonalResult = true;
                }
            }
            else
            {
                SequenceCounter = 0;
            }
        }

        return IsConnectFourInLeftDiagonalResult;
    }

    private bool isConnectFourInRightDiagonal(Cell i_LastMoveCell, int i_LastMoveRow, int i_LastMoveCol)
    {
        bool IsConnectFourInRightDiagonalResult = false;
        int StartRow = i_LastMoveRow;
        int StartCol = i_LastMoveCol;
        int CurrentCol = 0;
        int SequenceCounter = 0;

        while ((StartRow > 0) && (StartCol < m_BoardMatrixColSize - 1))
        {
            StartRow--;
            StartCol++;
        }

        CurrentCol = StartCol;

        for (int CurrentRow = StartRow; (CurrentRow < m_BoardMatrixRowSize) && (CurrentCol >= 0) && (IsConnectFourInRightDiagonalResult == false); CurrentRow++, CurrentCol--)
        {
            Cell CurrentCell = r_BoardMatrix[CurrentRow, CurrentCol];

            if (isSamePlayerSymbol(CurrentCell, i_LastMoveCell))
            {
                SequenceCounter++;

                if (SequenceCounter == k_ConnectedFour)
                {
                    IsConnectFourInRightDiagonalResult = true;
                }
            }
            else
            {
                SequenceCounter = 0;
            }
        }

        return IsConnectFourInRightDiagonalResult;
    }

    private bool isConnectFourInColumn(Cell i_LastMoveCell, int i_LastMoveCol)
    {
        bool IsConnectFourInColResult = false;
        int SequenceCounter = 0;
        Cell CurrentCellToCheck = null;

        for (int CurrentRowIndex = 0; CurrentRowIndex < m_BoardMatrixRowSize && IsConnectFourInColResult == false; CurrentRowIndex++)
        {
            CurrentCellToCheck = r_BoardMatrix[CurrentRowIndex, i_LastMoveCol];

            if (isSamePlayerSymbol(i_LastMoveCell, CurrentCellToCheck))
            {
                SequenceCounter++;

                if (SequenceCounter == k_ConnectedFour)
                {
                    IsConnectFourInColResult = true;
                }
            }
            else
            {
                SequenceCounter = 0;
            }
        }

        return IsConnectFourInColResult;
    }

    private bool isSamePlayerSymbol(Cell i_CellA, Cell i_CellB)
    {
        bool result = false;

        if (i_CellA.CellSymbol.Equals(i_CellB.CellSymbol))
        {
            result = true;
        }

        return result;
    }

    private bool isConnectFourInRow(Cell i_LastMoveCell, int i_LastMoveRow)
    {
        bool IsConnectFourInRowResult = false;
        int SequenceCounter = 0;
        Cell CurrentCellToCheck = null;

        for (int CurrentColIndex = 0; CurrentColIndex < m_BoardMatrixColSize && IsConnectFourInRowResult == false; CurrentColIndex++)
        {
            CurrentCellToCheck = r_BoardMatrix[i_LastMoveRow, CurrentColIndex];

            if (isSamePlayerSymbol(i_LastMoveCell, CurrentCellToCheck))
            {
                SequenceCounter++;

                if (SequenceCounter == k_ConnectedFour)
                {
                    IsConnectFourInRowResult = true;
                }
            }
            else
            {
                SequenceCounter = 0;
            }
        }

        return IsConnectFourInRowResult;
    }
}